package org.springframework.security.config.annotation.web.configurers;

import com.umakr.ax.security.service.SecurityUserDetailsServiceImpl;
import com.umakr.ax.utils.WebUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import org.apache.commons.lang.StringUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.codec.Base64;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.security.web.util.matcher.RequestMatcher;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
* @author gx
* @since 2017/5/22 11:07
* 通过header
*/
public class TokenLoginConfigurer <B extends HttpSecurityBuilder<B>> extends AbstractHttpConfigurer<HttpBasicConfigurer<B>,B> {
    private static final String HEADER_AUTH_TOKEN_NAME= "Authorization";
    private AuthenticationManager authenticationManager;
    private SecurityUserDetailsServiceImpl securityUserDetailsServiceImpl;
    public TokenLoginConfigurer(B builder){
        setBuilder(builder);
    }

    @SuppressWarnings("unchecked")
    @Override
    public void init(B http) throws Exception {

        ExceptionHandlingConfigurer<B> exceptionHandling = http.getConfigurer(ExceptionHandlingConfigurer.class);
        if(exceptionHandling == null) {
            return;
        }
        exceptionHandling.defaultAuthenticationEntryPointFor(new AuthenticationEntryPoint() {
            @SuppressWarnings("RedundantStringToString")
            @Override
            public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
                Authentication authentication =  attemptAuthentication(request);
                if(authentication.isAuthenticated()){
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                    request.getRequestDispatcher(request.getRequestURI()).forward(request,response);
                }
            }
        },new RequestMatcher() {
            @Override
            public boolean matches(HttpServletRequest request) {
                return StringUtils.isNotEmpty(request.getHeader(HEADER_AUTH_TOKEN_NAME));
            }
        });
    }

    @Override
    public void configure(B http) throws Exception {
        authenticationManager = http.getSharedObject(AuthenticationManager.class);
        securityUserDetailsServiceImpl = WebUtil.getBean(SecurityUserDetailsServiceImpl.class);
    }
    public Authentication attemptAuthentication(HttpServletRequest request) throws IOException{
        String header = request.getHeader(HEADER_AUTH_TOKEN_NAME);
        String[] tokens = extractAndDecodeHeader(header);
        assert tokens.length == 1;

        String username = tokens[0];
        if (username == null) {
            username = "";
        }
        username = username.trim();
        UserDetails userDetails = securityUserDetailsServiceImpl.loadUserByUsername(username);
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                userDetails, null, userDetails.getAuthorities());
        authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(
                request));
        return authentication;
    }

    private AuthenticationManager getAuthenticationManager() {
        return authenticationManager;
    }

    private String[] extractAndDecodeHeader(String header) throws IOException {

        //Bearer
        byte[] base64Token = header.substring(6).getBytes("UTF-8");
        String token = "";
        try {
            byte[] decoded  = Base64.decode(base64Token);
            token = new String(decoded, getCredentialsCharset());
        } catch (IllegalArgumentException e) {
            throw new BadCredentialsException("Failed to decode basic authentication token");
        }
        String key = "";
        try{
            Jws<Claims> jws =  Jwts.parser().setSigningKey(key).parseClaimsJws(token);
            String subject = jws.getBody().getSubject();
            return new String[]{subject};
        }catch (Exception e){
            throw new BadCredentialsException("Invalid authentication token");
        }
    }
    private String credentialsCharset = "UTF-8";
    protected String getCredentialsCharset(){
        return credentialsCharset;
    }
}
